#include <iostream>
using std::ostream;
using std::cout;
using std::endl;
#include <string>
#include <cstddef>
using std::size_t;

//private class for use by U_Has_Ptr_Old only
class U_Ptr_Old {
	friend class U_Has_Ptr_Old;
	int *ip;
	size_t use;
	U_Ptr_Old(int *p) :
			ip(p), use(1) {
	}
	~U_Ptr_Old() {
		delete ip;
	}
};

class U_Has_Ptr_Old {
public:
	// U_Has_Ptr_Old owns the pointer; p must have been dynamically allocated
	U_Has_Ptr_Old(int *p, int i) :
			ptr(new U_Ptr_Old(p)), val(i) {
	}

	// copy members and increment the use count
	U_Has_Ptr_Old(const U_Has_Ptr_Old &orig) :
			ptr(orig.ptr), val(orig.val) {
		++ptr->use;
	}
	U_Has_Ptr_Old& operator=(const U_Has_Ptr_Old&);

	// if use count goes to zero, delete the U_Ptr_Old object
	~U_Has_Ptr_Old() {
		if (--ptr->use == 0)
			delete ptr;
	}

	friend ostream& operator<<(ostream&, const U_Has_Ptr_Old&);
	// copy control and constructors as before

	// accessors must change to fetch value from U_Ptr_Old object
	int *get_ptr() const {
		return ptr->ip;
	}
	int get_int() const {
		return val;
	}

	// change the appropriate data member
	void set_ptr(int *p) {
		ptr->ip = p;
	}
	void set_int(int i) {
		val = i;
	}

	// return or change the value pointed to, so ok for const objects
	// Note: *ptr->ip is equivalent to *(ptr->ip)
	int get_ptr_val() const {
		return *ptr->ip;
	}
	void set_ptr_val(int i) {
		*ptr->ip = i;
	}
private:
	U_Ptr_Old *ptr;        // points to use-counted U_Ptr_Old class
	int val;
};

U_Has_Ptr_Old& U_Has_Ptr_Old::operator=(const U_Has_Ptr_Old &rhs) {
	++rhs.ptr->use;     // increment use count on rhs first
	if (--ptr->use == 0)
		delete ptr;    // if use count goes to 0 on this object, delete it
	ptr = rhs.ptr;      // copy the U_Ptr_Old object
	val = rhs.val;      // copy the int member
	return *this;
}

ostream& operator<<(ostream &os, const U_Has_Ptr_Old &hp) {
	os << "*ptr: " << hp.get_ptr_val() << "\tval: " << hp.get_int() << endl;
	return os;
}

int smart_ptr_old() {
	int obj = 0;

	U_Has_Ptr_Old ptr1(&obj, 42);
	U_Has_Ptr_Old ptr2(ptr1);
	cout << "(1) ptr1: " << ptr1 << endl << "ptr2: " << ptr2 << endl;

	ptr1.set_ptr_val(42); // sets object to which both ptr1 and ptr2 point
	ptr2.get_ptr_val();   // returns 42

	cout << "(2) ptr1: " << ptr1 << endl << "ptr2: " << ptr2 << endl;

	ptr1.set_int(0);   // changes s member only in ptr1
	ptr2.get_int();    // returns 42
	ptr1.get_int();    // returns 0

	cout << "(3) ptr1: " << ptr1 << endl << "ptr2: " << ptr2 << endl;
}

void f3_smart_old() {
	int obj;
	U_Has_Ptr_Old p1(&obj, 42);
	U_Has_Ptr_Old p2(p1);  // p1 and p2 both point to same int object
	U_Has_Ptr_Old p3(p1);  // p1, p2, and p3 all point to same int object
}

//int main(){
//	return smart_ptr_old();
//}
